using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Text;
#if TEST
using Xunit;
#endif

namespace Fadd.Commands
{
	/// <summary>
	/// Class for building information strings about a proxy property
	/// </summary>
	public class ObjectProxyProperty
	{
		private readonly static ObjectProxyTypeNameGenerator _typeNameGenerator = new ObjectProxyTypeNameGenerator();

		#region Property declaration string : PropertyDeclaration

		private const string PropertyDeclaration =
@"
		public {virtual} {returnType} {name}
		{
			{set}

			{get}
		}";

		private const string PropertySetDeclaration =
@"set
			{
				ObjectProxyCommand cmd = new ObjectProxyCommand(_realType, {signature}Set, new object[]{value});
				_dispatcher.Invoke(this, cmd);
				if (!cmd.IsHandled)
					throw new NotImplementedException(""{fullName}.{methodName} have not been implemented."");
				if (cmd.Exception != null)
					throw cmd.Exception;
			}";

		private const string PropertyGetDeclaration =
@"get
			{
				ObjectProxyCommand cmd = new ObjectProxyCommand(_realType, {signature}Get, new object[]{});
				_dispatcher.Invoke(this, cmd);
				if (!cmd.IsHandled)
					throw new NotImplementedException(""{fullName}.{methodName} have not been implemented."");
				if (cmd.Exception != null)
					throw cmd.Exception;

				return ({returnType})cmd.ReturnValue;
			}";

		#endregion

		private readonly PropertyInfo _propertyInfo;
		private readonly string _name;
		private readonly string _returnType;
		private readonly string _declaration;
		private readonly string _signature;
		private readonly Type _type;
		private readonly bool _hasGet;
		private readonly bool _hasSet;

		/// <summary>
		/// Instantiates and builds information about a property
		/// </summary>
		/// <param name="propertyInfo"></param>
		public ObjectProxyProperty(PropertyInfo propertyInfo)
		{
			Check.Require(propertyInfo, "propertyInfo");

			_propertyInfo = propertyInfo;
			_name = propertyInfo.Name.Trim();
			_returnType = _typeNameGenerator.GetGenericName(propertyInfo.PropertyType);
			_type = propertyInfo.PropertyType;
			_signature = ("_" + _name[0]).ToLower() + _name.Substring(1);
			_hasSet = _propertyInfo.CanWrite;
			_hasGet = _propertyInfo.CanRead;

			// Build the declaration of the method
			_declaration = BuildDeclaration(this);
		}

		/// <summary>
		/// Builds a declaration string for the property proxy
		/// </summary>
		/// <param name="property">The property to build a proxy declaration for</param>
		private static string BuildDeclaration(ObjectProxyProperty property)
		{
			string get = property._propertyInfo.CanRead ? PropertyGetDeclaration : string.Empty;
			string set = property._propertyInfo.CanWrite ? PropertySetDeclaration : string.Empty;

			string declaration = PropertyDeclaration;
			declaration = declaration.Replace("{set}", set);
			declaration = declaration.Replace("{get}", get);

			declaration = declaration.Replace("{returnType}", property._returnType.Trim());
			declaration = declaration.Replace("{fullName}", _typeNameGenerator.GetFullName(property._propertyInfo.DeclaringType));
			declaration = declaration.Replace("{signature}", property._signature);
			declaration = declaration.Replace("{name}", property._name);
			declaration = declaration.Replace("{methodName}", property._name);
			declaration = declaration.Replace("{virtual}", property._propertyInfo.DeclaringType.IsInterface ? string.Empty : "override");

			return declaration;
		}

		/// <summary>
		/// Retrieves a signature for the property, ie _propertyName
		/// </summary>
		public string Signature
		{
			get { return _signature; }
		}

		/// <summary>
		/// Retrieves the property name
		/// </summary>
		public string Name
		{
			get { return _name; }
		}

		/// <summary>
		/// Retrieves the return type for the property
		/// </summary>
		public string ReturnType
		{
			get { return _returnType; }
		}

		/// <summary>
		/// Retrieves the property declaration string
		/// </summary>
		public string Declaration
		{
			get { return _declaration; }
		}

		/// <summary>
		/// Retrieves the property type
		/// </summary>
		public Type PropertyType
		{
			get { return _type; }
		}

		/// <summary>
		/// Returns true if the property has a get field
		/// </summary>
		public bool HasGet
		{
			get { return _hasGet; }
		}

		/// <summary>
		/// Returns true if the property has a set field
		/// </summary>
		public bool HasSet
		{
			get { return _hasSet; }
		}

		#region Tests

		private interface IMyInterface
		{
			List<List<string>> Values { get; set; }

			List<int> ValueList { get; }

			int Id { set; }
		}

#if TEST
		[Fact]
		private static void TestGenericInterface()
		{
			Type interfaceType = typeof (IMyInterface);
			ObjectProxyProperty property = new ObjectProxyProperty(interfaceType.GetProperty("Values"));
            
			Assert.Equal("Values", property.Name);
			Assert.Equal("List<List<String>>", property.ReturnType);
			Assert.Equal("_values", property.Signature);
			Assert.Equal(@"
		public  List<List<String>> Values
		{
			set
			{
				ObjectProxyCommand cmd = new ObjectProxyCommand(_realType, _valuesSet, new object[]{value});
				_dispatcher.Invoke(this, cmd);
				if (!cmd.IsHandled)
					throw new NotImplementedException(""Fadd.Commands.IMyInterface.Values have not been implemented."");
				if (cmd.Exception != null)
					throw cmd.Exception;
			}

			get
			{
				ObjectProxyCommand cmd = new ObjectProxyCommand(_realType, _valuesGet, new object[]{});
				_dispatcher.Invoke(this, cmd);
				if (!cmd.IsHandled)
					throw new NotImplementedException(""Fadd.Commands.IMyInterface.Values have not been implemented."");
				if (cmd.Exception != null)
					throw cmd.Exception;

				return (List<List<String>>)cmd.ReturnValue;
			}
		}", property.Declaration);
		}
#endif
		#endregion
	}
}
